// Connect the gy-521 accelerometer to a digispark attyiny85 and light an LED when tipping it to the side (along X)
// Wire this up with gy-521:attiny85 as vcc:5v, gnd:gnd, sda:p0, scl:p3; sda and scl each have a 4.7k pull-up resistor

//#include <SoftSerial.h>     /* Allows Pin Change Interrupt Vector Sharing */
//#include <TinyPinChange.h>  /* Ne pas oublier d'inclure la librairie <TinyPinChange> qui est utilisee par la librairie <RcSeq> */
//
//SoftSerial mySerial(1, 3); // RX, TX




// The pin that is connected to the NeoPixels
#define PIN 4
 
// The amount of LEDs in the NeoPixels
#define PixelCount 72
const uint16_t PixelLength = PixelCount / 6;  // how many pixels in each face

#include <Adafruit_NeoPixel.h>
// Set up the NeoPixel library
Adafruit_NeoPixel strip = Adafruit_NeoPixel(PixelCount, PIN, NEO_GRB + NEO_KHZ800);


#define PEAK 12000

int lastPos = -1;

int times[] = { 
  1 * 60 ,        // 1 min,   0, front
  10 * 60 ,       // 10 min,  1, back
  5 * 60 ,        // 5 min,   2, right
  25 * 60 ,       // 25 min,  3, left
  40 * 60 ,       // 40 min,  4, bottom
  10              // 10 s,    5, top
};

int BRIGHT = 50;

uint32_t COLOR_BLACK =  strip.Color( 0, 0, 0 );

uint32_t colors[] = { 
  strip.Color( BRIGHT, BRIGHT, 0 ),
  strip.Color( BRIGHT, 0, BRIGHT ),
  strip.Color( 0, BRIGHT, BRIGHT ),
  strip.Color( BRIGHT, 0, 0 ),
  strip.Color( 0, BRIGHT, 0 ),
  strip.Color( 0, 0, BRIGHT )
};


bool started = false;
int face = -1;

int flag = 2000;

int lastFace = -1;
int lastFaceTime = 0;

int timeLeft = 0;
int totalTime = 0;
int timeStep = 1; // ms;
int fadeFlag = -1;
int fadeStep = 10;
int stepsRan = 0;
int lastStep = -1;


void fillAll(uint32_t c){
  for(int i=0; i<PixelCount; i++){ 
     if( started && ( i>= face * PixelLength && i< (face+1) * PixelLength ) ){
       // skip current running
     }else{      
       strip.setPixelColor(i, c); 
     }
  }
  strip.show();  
}

void doFlash(){  
    int beepPin = 0;
    pinMode(beepPin, OUTPUT);

    bool beep = false;
    
    // flash 
    for (int i=0; i<=5; i++){
      beep = i % 2 == 0;
      delay( 300 );
      fillAll( beep? colors[face] : COLOR_BLACK );
      digitalWrite(beepPin, beep?HIGH : LOW);      
    }
}

uint32_t darken( uint32_t c, int d ){
  uint32_t r = ( c & 0x0F00 ) >> 16;
  uint32_t g = ( c & 0x00F0 ) >> 8;
  uint32_t b = ( c & 0x000F );
  r = r > d? r - d : 0;
  g = g > d? g - d : 0;
  b = b > d? b - d : 0;
  return ((uint32_t)r << 16) | ((uint32_t)g <<  8) | b;
}

void doFade(){
  if(!started) return;
  if( millis()/1000 < fadeFlag + 1 ) return;
  fadeFlag = millis()/1000;

  timeLeft -= timeStep;
  
  if(timeLeft<=0){
    started = false;
    strip.setPixelColor(lastStep, COLOR_BLACK ); 
    strip.show();
      
    doFlash();
    face = -1;    
    return;
  } // time end

  int used = totalTime - timeLeft;  

  int i = used * PixelLength / totalTime ; // index of being faded
  
  i = i + face * PixelLength  ; 
  
  stepsRan++;
  if( lastStep != i ){
    strip.setPixelColor(lastStep, COLOR_BLACK );     
    lastStep = i;
    stepsRan=1;
  }
  
  uint32_t color = strip.getPixelColor(i);
  // color.Darken( fadeStep );   
  color = darken( color, fadeStep );
  strip.setPixelColor(i, color);     

  strip.show();  
}

void showFace(int p){
  uint32_t on = strip.Color(10, 10, 0); 
  uint32_t off = COLOR_BLACK;
  for (int i = 0; i < PixelCount; i++)
  {
    if(  started && ( i>= face * PixelLength && i< (face+1) * PixelLength ) ){      
     // // skip current face
    }else if(i>= p * PixelLength && i< (p+1) * PixelLength){ 
      strip.setPixelColor(i, on); 
    }else{
      strip.setPixelColor(i, off); 
    }
  }
  strip.show(); 
}

int getTimes(int i){
  
  if(i==0) return 12 ; // * 60;     // ,        // 1 min,   0, front
  if(i==1) return   24 ; // * 60;   //  ,       // 10 min,  1, back
  if(i==2) return   36 ; // * 60;   // ,        // 5 min,   2, right
  if(i==3) return   48 ; // * 60;   //  ,       // 25 min,  3, left
  if(i==4) return   60 ; // * 60;  // ,       // 40 min,  4, bottom
  return  72;                 // 10 s,    5, top
 
}

void doStart(int p){

  if( p == lastPos ) return;
  lastPos = p;
  started = true;
  face = p;
   
  uint32_t on = colors[p]; 
  uint32_t off = strip.Color(0, 0, 0);
  for (int i = 0; i < PixelCount; i++)
  {
    if( i>= p * PixelLength && i< (p+1) * PixelLength){ 
      strip.setPixelColor(i, on); 
    }else{      
      strip.setPixelColor(i, off);
    } 
  }
  strip.show();
  timeLeft = 1 * getTimes(face) ; // times[face];
  totalTime = timeLeft;
  fadeStep = 1 + timeStep * PixelLength * 255 / totalTime; 
}






#include <TinyWireM.h>


const int MPU_addr=0x68;  // I2C address of the MPU-6050
int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ;

void mpu_init() {
  TinyWireM.beginTransmission(MPU_addr);
  TinyWireM.send(0x6B);  // PWR_MGMT_1 register
  TinyWireM.send(0);     // set to zero (wakes up the MPU-6050)
  TinyWireM.endTransmission();
}

void mpu_read() {
  TinyWireM.beginTransmission(MPU_addr);
  TinyWireM.send(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
  TinyWireM.endTransmission();
  TinyWireM.requestFrom(MPU_addr, 14);  // request a total of 14 registers
  AcX=TinyWireM.receive()<<8|TinyWireM.receive();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
  AcY=TinyWireM.receive()<<8|TinyWireM.receive();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  AcZ=TinyWireM.receive()<<8|TinyWireM.receive();  // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
  Tmp=TinyWireM.receive()<<8|TinyWireM.receive();  // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
  GyX=TinyWireM.receive()<<8|TinyWireM.receive();  // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
  GyY=TinyWireM.receive()<<8|TinyWireM.receive();  // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
  GyZ=TinyWireM.receive()<<8|TinyWireM.receive();  // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)



  /* Print out the values */
//  mySerial.print(AcX);   // AcX > 12000 when it is on top
//  mySerial.print(",");
//  mySerial.print(AcY);
//  mySerial.print(",");
//  mySerial.print(AcZ);
//  mySerial.print(", ");
//  mySerial.print(GyX);
//  mySerial.print(",");
//  mySerial.print(GyY);
//  mySerial.print(",");
//  mySerial.print(GyZ);
//  mySerial.println("");


}

void setup() {
//  mySerial.begin(115200);
//  delay(1000);
//   mySerial.println("Hello, world?");


  strip.begin();
  
  TinyWireM.begin();
  pinMode(1, OUTPUT); //LED on Model A
  mpu_init();
}

void loop() {
  mpu_read();


  // to find the face on top
  float vv[] = {AcX, AcY, AcZ};
  int j=-1;
  int v=-1;
  for ( int i=0; i<3; i++ ){
    if( abs( vv[i] ) > PEAK ){
      j = i;
      break;
    }
  }

  if( j>=0 ){
    
    v=j*2;
    if( vv[j] < 0 ){
      v += 1;
    }

//    mySerial.print("current faceIndex="); mySerial.println(v);

    if( abs( vv[j] ) > PEAK * 1.5 ){ // only start when shake hard
      doStart(v);    
    }else if(!started & v == lastFace && millis()/1000> (lastFaceTime + 5) ){ 
      fillAll( COLOR_BLACK );  // hide all when 5s no moving
    }else if( v!= lastFace ){    // to show which face is on
      showFace(v);  
    } 

    if( lastFace != v ){
      lastFaceTime = millis()/1000;
      lastFace = v;
    }
       
  } 
  // not found face on top
  else if(!started & millis()/1000> (lastFaceTime + 5) ){ 
      fillAll( COLOR_BLACK );  // hide all when 5s no moving
  }

  doFade();

  delay(10);
}